package data

import (
	"context"
	"errors"
	"fmt"
	"github.com/gogf/gf/util/gconv"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/trace"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"gorm_transaction/internal/biz"
)

// 不在事务中执行，初始化自己的db
func (a *amount1Repo) EditOrderMain(ctx context.Context, id uint32, changeValue int64) error {
	db := a.data.Mysql.Table(biz.OrderMainTableName).WithContext(ctx)

	// stock = stock + changeValue Notice 并发安全的修改方式
	err := db.Where("id = ?", id).UpdateColumn("stock", gorm.Expr("stock + ?", changeValue)).Error

	return err
}

// 在事务中执行，这里的db其实是ctx中携带的tx（在InTx方法中加进去的）
func (a *amount1Repo) EditOrderMainWithTX(ctx context.Context, id uint32, changeValue int64) error {

	// 注意需要指定表名
	db := a.data.DB(ctx).Table(biz.OrderMainTableName)

	// stock = stock + changeValue Notice 并发安全的修改方式
	err := db.Where("id = ?", id).UpdateColumn("stock", gorm.Expr("stock + ?", changeValue)).Error

	return err
}

func (a *amount1Repo) AddOrderMainOnConflictWithInnerTX(ctx context.Context, orderMainModel *biz.OrderMain, orderNo string) error {

	err := a.data.DB(ctx).Transaction(func(tx *gorm.DB) error {
		// order_main表: 主键id冲突就update，不冲突就insert
		// Notice 如果操作只有上面的 on duplicate key update 操作的话，就不用把这个操作放到事务中执行了！这个语句本身就是在事务中执行的！
		errOnConflict := tx.Table(biz.OrderMainTableName).Clauses(clause.OnConflict{
			// 冲突的字段 Notice 需要在数据库中建立 unique_code 这个字段的唯一索引
			Columns: []clause.Column{{Name: "unique_code"}},
			// 冲突后执行的操作
			DoUpdates: clause.Assignments(map[string]interface{}{
				"stock": gorm.Expr("stock + ?", orderMainModel.Stock),
			}),
			// DoNothing: true, // 指定的字段有冲突不做任何操作，用这里，并且把DoUpdates注释掉！
		}).Create(orderMainModel).Error // 不冲突就Create
		if errOnConflict != nil {
			return errors.New(fmt.Sprintf("errOnconflict! err: %v", gconv.String(errOnConflict)))
		}

		// 再在流水表中新增一条记录
		statement := biz.OrderStatement{
			OrderMainId: orderMainModel.Id,
			OrderNo:     orderNo,
			ChangeValue: orderMainModel.Stock,
		}
		errAddStatement := tx.Table(biz.OrderStatementTableName).Create(&statement).Error
		if errAddStatement != nil {
			return errors.New(fmt.Sprintf("添加流水记录时出错了! err: %v", errAddStatement))
		}

		return nil
	})

	if err != nil {
		return err
	}

	return nil
}

func (a *amount1Repo) GetOrderMainByIdWithInnerTX(ctx context.Context, id uint32) (*biz.OrderMain, error) {
	ctx, span := otel.Tracer("DATA").Start(ctx, "GetOrderMainByIdWithInnerTXDATA", trace.WithSpanKind(trace.SpanKindInternal))
	defer span.End()

	orderMain := new(biz.OrderMain)
	err := a.data.DB(ctx).Transaction(func(tx *gorm.DB) error {
		errTake := tx.Table(biz.OrderMainTableName).Where("id = ?", id).Take(orderMain).Error
		fmt.Println("errTake: ", errTake)

		// Notice 模拟延迟～
		//time.Sleep(time.Second * 2)

		return errTake
	})

	if err != nil {
		return nil, errors.New(fmt.Sprintf("查询order_main失败! id: %v, err: %v", id, err))
	}

	return orderMain, nil
}

func (a *amount1Repo) GetOrderMainByIdWithTX(ctx context.Context, id uint32) (*biz.OrderMain, error) {

	db := a.data.DB(ctx).Table(biz.OrderMainTableName)
	orderMain := new(biz.OrderMain)

	err := db.Where("id = ?", id).Take(orderMain).Error

	if err != nil {
		return nil, errors.New(fmt.Sprintf("查询order_main失败! err: %v", err))
	}

	return orderMain, nil
}

func (a *amount1Repo) EditOrderMainSuperOrderWithTX(ctx context.Context, id uint32) error {

	// 注意需要指定表名
	db := a.data.DB(ctx).Table(biz.OrderMainTableName)

	err := db.Where("id = ?", id).UpdateColumn("super_order", 1).Error

	return err
}
